<?php


class SignatureAnnotation {
	
	const PARAMETER_ANNOTATION_RE = '/@param\s*(?<types>\S*)\s*\$(?<name>\w+)\s*(?<documentation>[^@]*)/m';
	const RETURN_ANNOTATION_RE = '/@return\s*(?<type>\S*)\s*(?<documentation>[^@]*)/m';
	
	protected $signature_tree = array();
	
	public function __construct(ReflectionFunctionAbstract $reflection) {
		$doc_comment = $this->sanitize($reflection->getDocComment());
		
		$this->documentation = $this->parseDocumentationAnnotation($doc_comment);
		$this->return_annotation = $this->parseReturnAnnotation($doc_comment);
		$this->parameter_annotations = $this->parseParameterAnnotations($doc_comment);

	}
	
	public function returnTypes() {
		return $this->return_annotation['types'];
	}
	
	public function documentation() {
		return $this->documentation;
	}
	
	public function signatures() {
		$types = array();
		foreach ($this->parameter_annotations as $annotation)
			$types[] = $annotation['types'];
		
		$type_combinations = array();
		$this->combine($types, array(), 0, $type_combinations);
		
		$signatures = array();
		foreach ($type_combinations as $combination)
			$signatures[] = array_combine(array_keys($this->parameter_annotations), $combination);
			
		return $signatures;
	}
	
	protected function combine($types, $accumulator, $offset, &$combinations) {
		if ($offset >= count($types))
			$combinations[] = $accumulator;
		else
			foreach ($types[$offset] as $type)
				$this->combine($types, array_merge($accumulator, array($type)), $offset + 1, $combinations);
	}
	
	
	
	protected function sanitize($doc_comment) {
		return trim(preg_replace('/^\s*[\/\*]*/m', '', $doc_comment));
	}
	
	protected function parseDocumentationAnnotation($doc_comment) {
		$matches = array();
		$match_count = preg_match('/^([^@]+)/m', $doc_comment, $matches);

		return isset($matches[1]) ? trim($matches[1]) : '';
	}
	
	protected function parseReturnAnnotation($doc_comment) {
		$matches = array();
		$match_count = preg_match(self::RETURN_ANNOTATION_RE, $doc_comment, $matches);
		if ($match_count === false) return NULL;
		return array(
			'types' => explode('|', $matches['type']),
			'documentation' => $this->sanitizeDocumentation($matches['documentation']),
		);
	}
	
	protected function parseParameterAnnotations($doc_comment) {
		$matches = array();
		$match_count = preg_match_all(self::PARAMETER_ANNOTATION_RE, $doc_comment, $matches);
		if ($match_count === false) return NULL;
		$parameters = array();
		foreach ($matches['name'] as $i => $name) {
			$parameters[$name] = array(
				'types' => explode('|', $matches['types'][$i]),
				'documentation' => $this->sanitizeDocumentation($matches['documentation'][$i])
			);
		}
		return $parameters;
	}
	
	protected function sanitizeDocumentation($documentation) {
		return trim(str_replace("\n", '', $documentation));
	}
	
}


